﻿/*
 *       This file is part of VFC.
 *
 *        VFC is free software: you can redistribute it and/or modify
 *        it under the terms of the GNU General Public License as published by
 *        the Free Software Foundation, either version 3 of the License, or
 *        (at your option) any later version.
 *
 *        VFC is distributed in the hope that it will be useful,
 *        but WITHOUT ANY WARRANTY; without even the implied warranty of
 *        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *        GNU General Public License for more details.
 *
 *        You should have received a copy of the GNU General Public License
 *        along with VFC.  If not, see <http://www.gnu.org/licenses/>.
 */


using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;

namespace VFC.Nes.Cpu
{
    class Memory
    {
        public static volatile byte[] MemoryMap = new byte[0x10000]; //Holds all the memory content for the cpu...

        private static UInt16 MSB, LSB, TMP;
        private static StringBuilder sb = new StringBuilder();
        private static StringBuilder sb2 = new StringBuilder();
        private static StringBuilder sb3 = new StringBuilder();
        private static string HEX;
        private static string[] _HEX_;

        public static byte Read(byte addr)
        {
            if(Config.App.EnableReadMsg)
            {
                Emu.Misc.ReadMsg(addr);
            }
            return MemoryMap[addr & 0xff];
        }

        public static byte Read(int addr)
        {
            if (Config.App.EnableReadMsg)
            {
                Emu.Misc.ReadMsg(addr);
            }
            return MemoryMap[addr & 0xffff];
        }

        public static byte Read(UInt16 addr)
        {
            if (Config.App.EnableReadMsg)
            {
                Emu.Misc.ReadMsg(addr);
            }
            return MemoryMap[addr & 0xffff];
        }

        public static void Write(byte addr, byte value)
        {
            if (Config.App.EnableWriteMsg)
            {
                Emu.Misc.WriteMsg(addr, value);
            }
            MemoryMap[addr & 0xff] = value;
        }

        public static void Write(int addr, byte value)
        {
            if (Config.App.EnableWriteMsg)
            {
                Emu.Misc.WriteMsg(addr, value);
            }
            MemoryMap[addr & 0xffff] = value;
        }

        public static void Write(UInt16 addr, byte value)
        {
            if (Config.App.EnableWriteMsg)
            {
                Emu.Misc.WriteMsg(addr, value);
            }
            MemoryMap[addr & 0xffff] = value;
        }

        public static void Write(byte addr, int value)
        {
            if (Config.App.EnableWriteMsg)
            {
                Emu.Misc.WriteMsg(addr, value);
            }
            MemoryMap[addr & 0xff] = (byte)value;
        }

        public static void Write(int addr, int value)
        {
            if (Config.App.EnableWriteMsg)
            {
                Emu.Misc.WriteMsg(addr, value);
            }
            MemoryMap[addr & 0xffff] = (byte)value;
        }

        public static void Write(UInt16 addr, int value)
        {
            if (Config.App.EnableWriteMsg)
            {
                Emu.Misc.WriteMsg(addr, value);
            }
            MemoryMap[addr & 0xffff] = (byte)value;
        }

        public static void Init()
        {
            //***********************************************//
            //           Memory Power-Up state...            //
            //***********************************************//

            for (UInt16 ctr = 0x0000; ctr <= 0x07ff; ctr++)
            {
                if (ctr == 0x0008)
                {
                    Write(ctr, 0xf7);
                }
                else if (ctr == 0x0009)
                {
                    Write(ctr, 0xef);
                }
                else if (ctr == 0x000a)
                {
                    Write(ctr, 0xdf);
                }
                else if (ctr == 0x000f)
                {
                    Write(ctr, 0xbf);
                }
                else
                {
                    Write(ctr, 0xff);
                }
            }

            //FastWrite(0x4017, 0x00); //frame irq enabled...
            //FastWrite(0x4015, 0x00); //all channels disabled...

            //FIXME: not sure about $4010-$4013...
            //for (UInt16 ctr = 0x4000; ctr <= 0x400F; ctr++)
            //{
                //FastWrite(ctr, 0x00);
            //}
        }

        public static void Clear()
        {
            Array.Clear(MemoryMap, 0, MemoryMap.Length);
        }

        public static UInt16 GetResetVector()
        {
            //*********************************************
            //               RESET VECTOR
            //    This vector is located at $FFFC-$FFFD
            //*********************************************
            MSB = LSB = TMP = 0x0000;

            MSB = Read(0xfffd);
            LSB = Read(0xfffc);

            TMP = (UInt16)((MSB << 8) | LSB);
            return TMP;
        }

        public static UInt16 GetNmiVector()
        {
            //*********************************************
            //               NMI VECTOR
            //    This vector is located at $FFFA-$FFFB
            //*********************************************
            MSB = LSB = TMP = 0x0000;

            MSB = Read(0xfffb);
            LSB = Read(0xfffa);

            TMP = (UInt16)((MSB << 8) | LSB);
            return TMP;
        }

        public static UInt16 GetIrqVector()
        {
            //*********************************************
            //               IRQ/BRK VECTOR
            //    This vector is located at $FFFE-$FFFF
            //*********************************************
            MSB = LSB = TMP = 0x0000;

            MSB = Read(0xffff);
            LSB = Read(0xfffe);

            TMP = (UInt16)((MSB << 8) | LSB);
            return TMP;
        }

        public static int GetMemoryPage(UInt16 addr)
        {
            //*********************************************
            //        This Function tells the Page
            //            of the Memory address
            //*********************************************
            return addr >> 8;
        }

        public static int GetMemoryPage(int addr)
        {
            //*********************************************
            //        This Function tells the Page
            //            of the Memory address
            //*********************************************
            return (addr & 0xffff) >> 8;
        }

        public static string GetMemoryContent(UInt16 start, UInt16 end)
        {
            int i = 0;

            HEX = BitConverter.ToString(MemoryMap);
            _HEX_ = HEX.Split('-');

            sb.Append(start.ToString("X4").PadLeft(4,'0') + ":  ");
            for (UInt16 ctr = start; ctr <= end; ctr++)
            {
                if (i <= 7)
                {
                    sb.Append(_HEX_[ctr]);
                    sb.Append(" ");
                    sb3.Append(Emu.Misc.StringHexToChar(_HEX_[ctr]));
                    sb3.Append(" ");
                    i++;
                    if (ctr == end) 
                    {
                        sb.Append("   ");
                        sb.Append(sb3.ToString());
                        sb2.AppendLine(sb.ToString());
                        break; 
                    }
                }
                else
                {
                    sb.Append("   ");
                    sb.Append(sb3.ToString());
                    sb2.AppendLine(sb.ToString());
                    sb.Length = 0;
                    sb3.Length = 0;
                    sb.Append(ctr.ToString("X4").PadLeft(4, '0') + ":  ");
                    i = 0;
                    ctr--;
                }
            }
            HEX = sb2.ToString();
            sb.Length = 0;
            sb2.Length = 0;
            sb3.Length = 0;
            return HEX;
        }
    }

}
